#ifndef _LOGGER_HPP_
#define _LOGGER_HPP_

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "util.hpp"
#include "level.hpp"
#include "sinker.hpp"
#include "logInfo.hpp"
#include "format.hpp"
#include "asyncLoggerCtrl.hpp"
#include <unordered_map>
#include <atomic>
#include <mutex>
#include <cstdarg>

namespace __mylog
{
const char* DEFAULT_LOGGER_NAME = "default";

/*日志器基类，组织各个模块进行日志的输出*/
class Logger
{
protected:
    std::string _name;                   // 日志器名称
    std::atomic<Level::Value> _minLevel; // 日志器最低输出等级
    Formatter::ptr _formatter;           // 格式化器
    std::vector<Sinker::ptr> _sinkers;   // 日志器的多个日志落地方向
    std::mutex _mutex;                   // 保护_sinkers的互斥锁

public:
    enum class Type
    {
        SYNC = 0, // 同步日志器
        ASYNC     // 异步日志器
    };
    using ptr = std::shared_ptr<Logger>;

protected:
    Logger(const std::string &name, Level::Value minLevel, Formatter::ptr formatter, std::vector<Sinker::ptr> sinkers)
        : _name(name), _minLevel(minLevel), _formatter(formatter), _sinkers(sinkers.begin(), sinkers.end())
    {
    }

public:
    /*把 DEBUG 等级的日志消息输出*/
    void debug(const char *file, size_t line, const char *format, ...)
    {
        Level::Value lv = Level::Value::DEBUG;
        // 0.是否可以输出
        if (levelAvailable(lv) == false)
            return;
        // 1.组织日志消息
        va_list al;
        va_start(al, format);
        std::string message = organizeMessage(lv, file, line, format, al);
        va_end(al);
        // 2.日志消息输出
        outPut(message);
    }
    /*把 INFO 等级的日志消息输出*/
    void info(const char *file, size_t line, const char *format, ...)
    {
        Level::Value lv = Level::Value::INFO;
        // 0.是否可以输出
        if (levelAvailable(lv) == false)
            return;
        // 1.组织日志消息
        va_list al;
        va_start(al, format);
        std::string message = organizeMessage(lv, file, line, format, al);
        va_end(al);
        // 2.日志消息输出
        outPut(message);
    }
    /*把 WARN 等级的日志消息输出*/
    void warn(const char *file, size_t line, const char *format, ...)
    {
        Level::Value lv = Level::Value::WARN;
        // 0.是否可以输出
        if (levelAvailable(lv) == false)
            return;
        // 1.组织日志消息
        va_list al;
        va_start(al, format);
        std::string message = organizeMessage(lv, file, line, format, al);
        va_end(al);
        // 2.日志消息输出
        outPut(message);
    }
    /*把 ERROR 等级的日志消息输出*/
    void error(const char *file, size_t line, const char *format, ...)
    {
        Level::Value lv = Level::Value::ERROR;
        // 0.是否可以输出
        if (levelAvailable(lv) == false)
            return;
        // 1.组织日志消息
        va_list al;
        va_start(al, format);
        std::string message = organizeMessage(lv, file, line, format, al);
        va_end(al);
        // 2.日志消息输出
        outPut(message);
    }
    /*把 FATAL 等级的日志消息输出*/
    void fatal(const char *file, size_t line, const char *format, ...)
    {
        Level::Value lv = Level::Value::FATAL;
        // 0.是否可以输出
        if (levelAvailable(lv) == false)
            return;
        // 1.组织日志消息
        va_list al;
        va_start(al, format);
        std::string message = organizeMessage(lv, file, line, format, al);
        va_end(al);
        // 2.日志消息输出
        outPut(message);
    }

public:
    void setMinLevel(const Level::Value &lv)
    {
        _minLevel = lv;
    }

protected:
    /*判断是否可以日志输出。当前等级大于或等于最小限制等级时才可以输出。*/
    bool levelAvailable(const Level::Value &lv)
    {
        return lv >= _minLevel;
    }

    /*提取日志有效信息并构造日志信息，返回格式化后的日志消息（辅助的函数）*/
    std::string organizeMessage(const Level::Value &lv, const char *file, size_t line, const char *format, va_list al)
    {
        // 1.提取出不定参中的日志有效载荷到message中
        char *buf;
        std::string message;
        int n = vasprintf(&buf, format, al);
        if (n < 0)
        {
            message = "Get log message failed.\n";
        }
        else
        {
            message.assign(buf, n);
            free(buf);
        }
        // 2.构造日志信息对象
        LogInfo info(lv, _name, file, line, message);

        // 3.调用Formatter格式化日志信息并返回
        return _formatter->run(info);
    }

    /*把日志消息输出的函数。具体如何输出，由派生的日志器类决定*/
    virtual void outPut(const std::string &message) = 0;
};

/****************************************End of class Logger****************************************/

/*同步日志器，继承Logger类*/
class SyncLogger : public Logger
{
public:
    SyncLogger(const std::string &name, Level::Value minLevel, Formatter::ptr formatter, std::vector<Sinker::ptr> sinkers)
        : Logger(name, minLevel, formatter, sinkers)
    {
    }

    /*把日志消息直接输出到指定的sinkers中。*/
    virtual void outPut(const std::string &message) override
    {
        //这里需要加锁，因为主线程可能有多个地方调用SyncLogger的输出函数，同时访问临界资源_sinkers
        std::unique_lock<std::mutex> lock(_mutex);
        if (!_sinkers.empty())
            for (auto &sinker : _sinkers)
                sinker->run(message.c_str(), message.size());
    }
};

/*异步日志器，继承Logger类*/
class AsyncLogger : public Logger
{
private:
    AsyncLoggerCtrl::ptr _ctrl; // 异步线程控制器，携带一个异步线程

public:
    AsyncLogger(const std::string &name, Level::Value minLevel, Formatter::ptr formatter, std::vector<Sinker::ptr> sinkers)
        : _ctrl(std::make_shared<AsyncLoggerCtrl>(std::bind(&AsyncLogger::sink, this, std::placeholders::_1)))
        , Logger(name, minLevel, formatter, sinkers)
    {
    }

private:
    /*把日志消息直接输出到缓冲区中（生产者），让异步线程去消费*/
    virtual void outPut(const std::string &message) override
    {
        // 直接往缓冲区放，日志的落地由异步工作线程完成
        _ctrl->push(message);
    }

    /*异步线程进行实际日志落地时的回调函数*/
    void sink(AsyncLoggerCharBuf &logMsg)
    {
        //这里不用加锁，因为一个AsyncLogger只有一个异步线程，即消费者只有一个，不需要维护消费者-消费者的互斥
        // std::unique_lock<std::mutex> lock(_mutex); 
        if (!_sinkers.empty())
            for (auto &sinker : _sinkers)
                sinker->run(logMsg.readBegin(), logMsg.readEnd());
    }
};

/*日志器建造者*/
class LoggerBuilder
{
protected:
    Logger::Type _type;                // 日志器类型
    std::string _name;                 // 日志器名称
    Level::Value _minLevel;            // 日志器最低输出等级
    Formatter::ptr _formatter;         // 格式化器
    std::vector<Sinker::ptr> _sinkers; // 日志器的多个日志落地方向

    LoggerBuilder()
        : _type(Logger::Type::SYNC), _minLevel(Level::Value::DEBUG)
    {
    }
public:
    using ptr = std::shared_ptr<LoggerBuilder>;

    /*设置日志器的各种属性*/
    void setLoggerName(const std::string &name) { _name = name; }
    void setMinLevel(const Level::Value &lv) { _minLevel = lv; }
    void setLoggerType(const Logger::Type &type) { _type = type; }
    void setFormatter(const std::string &fmt = DEFAULT_FORMAT) { _formatter = std::make_shared<Formatter>(fmt); }
    
    template <typename SinkType, typename... Args>
    void setSinker(Args &&...args)
    {
        auto sink = SinkerCreator::run<SinkType>(std::forward<Args>(args)...);
        _sinkers.push_back(sink);
    }

    /*建造日志器*/
    virtual Logger::ptr build() = 0;
};

/*默认日志器建造者（内部建造默认日志器使用）*/
class DefaultLoggerBuilder : public LoggerBuilder
{
public:
    virtual Logger::ptr build() override
    {
        if (_name.empty())
        {
            std::cout << "[ERRPR]: 默认日志器名称不能为空！\n";
            exit(-1);
        }
        if (_formatter.get() == nullptr)
            setFormatter();
        if (_sinkers.empty())
            setSinker<ConsoleSinker>();
        Logger::ptr lp;
        if (_type == Logger::Type::ASYNC)
            lp = std::make_shared<AsyncLogger>(_name, _minLevel, _formatter, _sinkers);
        else
            lp = std::make_shared<SyncLogger>(_name, _minLevel, _formatter, _sinkers);
        return lp;
    }
};

/*管理用户创建的所有日志器(单例模式)*/
class LoggerManager
{
private:
    std::mutex _mtx;                                       // 保护该类中公有接口的互斥锁
    Logger::ptr _defaultLogger;                            // 默认日志器
    std::unordered_map<std::string, Logger::ptr> _loggers; //<LoggerName, Logger>根据日志器名找日志器

private:
    LoggerManager()
    {
        // 单例日志器管理模块在构造时就建造默认日志器
        buildDefaultLogger();
    }

    /*建造默认日志器*/
    void buildDefaultLogger()
    {
        std::unique_ptr<DefaultLoggerBuilder> dlb(new DefaultLoggerBuilder());
        dlb->setLoggerName(DEFAULT_LOGGER_NAME);
        dlb->setLoggerType(Logger::Type::SYNC);
        _defaultLogger = dlb->build();
        _loggers.insert(std::make_pair(DEFAULT_LOGGER_NAME, _defaultLogger));
        std::cout << "[INFO]: 初始化默认日志器成功\n";
    }
    LoggerManager(const LoggerManager &) = delete;
    LoggerManager &operator=(const LoggerManager &) = delete;

public:
    /*获取日志管理者对象*/
    inline static LoggerManager &getInstance()
    {
        static LoggerManager e; // C++11编译器实现了静态局部变量构造时的线程安全
        return e;
    }
    /*添加新的日志器*/
    void add(const std::string &name, const Logger::ptr logger)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        _loggers.insert(std::make_pair(name, logger));
    }
    /*判断日志器是否存在*/
    bool exists(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        auto it = _loggers.find(name);
        return (it != _loggers.end());
    }
    /*获取指定的日志器*/
    Logger::ptr get(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        auto it = _loggers.find(name);
        if (it == _loggers.end())
            return defaultLogger();
        return it->second;
    }
    /*获取默认日志器*/
    Logger::ptr defaultLogger()
    {
        std::unique_lock<std::mutex> lock(_mtx);
        return _defaultLogger;
    }
};

/*用户日志器建造者（外部用户使用）*/
class UserLoggerBuilder : public LoggerBuilder
{
public:
    virtual Logger::ptr build() override
    {
        if (_name.empty())
        {
            std::cout << "[ERROR]: 日志器名称不能为空！\n";
            exit(-1);
        }
        if(_name == DEFAULT_LOGGER_NAME)
        {
            std::cout << "[ERROR]: 不能使用默认日志器名称";
            exit(-1);
        }
        if (_formatter.get() == nullptr)
        {
            std::cout << "[WARN]: 当前日志器[" << _name << "]未检测到日志格式，默认设置为[%t][%c][%n][%i][%f:%l]%T%m%N\n";
            setFormatter();
        }
        if (_sinkers.empty())
        {
            std::cout << "[WARN]: 当前日志器[" << _name << "]未检测到落地方向，默认设置为标准输出!\n";
            setSinker<ConsoleSinker>();
        }
        Logger::ptr lp;
        if (_type == Logger::Type::ASYNC)
            lp = std::make_shared<AsyncLogger>(_name, _minLevel, _formatter, _sinkers);
        else
            lp = std::make_shared<SyncLogger>(_name, _minLevel, _formatter, _sinkers);
        LoggerManager::getInstance().add(_name, lp);
        return lp;
    }
};

}

#endif